package zw_501_600.zw_509_斐波那契数;

/**
 * F(0) = 0，F(1) = 1
 * F(n) = F(n - 1) + F(n - 2)，其中 n > 1
 */
class Solution {

    public static void main(String[] args) {
        int n = 4;
        int num = fib(n);
        System.out.println(num);
    }


    /**
     * 方法二：矩阵快速幂
     * https://leetcode.cn/problems/fibonacci-number/solution/fei-bo-na-qi-shu-by-leetcode-solution-o4ze/
     * 方法一的时间复杂度是 O(n)。使用矩阵快速幂的方法可以降低时间复杂度。
     * 时间复杂度：O(logn)。
     * 空间复杂度：O(1)。
     *
     * @param n
     * @return
     */
    public static int fib(int n) {
        if (n < 2) {
            return n;
        }
        int[][] q = {{1, 1}, {1, 0}};
        int[][] res = pow(q, n - 1);
        return res[0][0];
    }

    public static int[][] pow(int[][] a, int n) {
        int[][] ret = {{1, 0}, {0, 1}};
        while (n > 0) {
            if ((n & 1) == 1) {
                ret = multiply(ret, a);
            }
            n >>= 1;
            a = multiply(a, a);
        }
        return ret;
    }

    public static int[][] multiply(int[][] a, int[][] b) {
        int[][] c = new int[2][2];
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
            }
        }
        return c;
    }


    /**
     * 方法一：动态规划
     * 时间复杂度：O(n)。
     * 空间复杂度：O(1)。
     *
     * @param n
     * @return
     */
//    public static int fib(int n) {
//        if (n < 2) return n;
//        int p = 0, q = 0, r = 1;
//        for (int i = 2; i <= n; ++i) {
//            p = q;
//            q = r;
//            r = p + q;
//        }
//        return r;
//    }


    /**
     * 递归
     *
     * @param n
     * @return
     */
//    public static int fib(int n) {
//        if (n < 2) return n;
//        return fib(n - 1) + fib(n - 2);
//    }
}
